package scuts.core;

import scuts.core.types.LazyIterator;


// Faster Fold Implementations for Arrays (no reverse needed)
/*
class FoldArray {
	public static function foldr<A,B>(arr:Array<A>, f:A->B->B, acc:B):B
	{
		var last = arr.length;
		while (--last > -1) {
			acc = f(arr[last], acc);
		}
		return acc;
	}
	
	public static function foldri<A,B>(arr:Array<A>, f:A->B->Int->B, acc:B):B
	{
		var last = arr.length;
		while (--last > -1) {
			acc = f(arr[last], acc, last);
		}
		return acc;
	}
	
	
	public static function foldli<A,B>(arr:Array<A>, f:B->A->Int->B, acc:B):B
	{
		for (i in 0...arr.length) {
			acc = f(acc, arr[i], i);
		}
		return acc;
	}
}
*/
class FoldLazyIterator 
{
  public static function foldRight<A,B>(iter:LazyIterator<A>, f:A->B->B, acc:B):B
	{
		return FoldIterator.foldRight(iter(), f, acc);
	}
	
	public static function foldRightWithIndex<A,B>(iter:LazyIterator<A>, f:A->B->Int->B, acc:B):B
	{
		return FoldIterator.foldRightWithIndex(iter(), f, acc);
	}
	/*
   * foldl<A,B>(iter:Iterator<A>, f:B->A->B, acc:B):B
   */
	public static function foldLeft<A,B>(iter:LazyIterator<A>, f:B->A->B, acc:B):B
	{
		return FoldIterator.foldLeft(iter(), f, acc);
		
	}
	
	public static function foldLeftWithIndex < A, B > (iter:LazyIterator<A>, f:B->A->Int->B, acc:B):B
  {
    return FoldIterator.foldLeftWithIndex(iter(), f, acc);
  }
}

class FoldIterator {
	public static function foldRight<A,B>(iter:Iterator<A>, f:A->B->B, acc:B):B
	{
		for (e in iter) {
			acc = f(e, acc);
		}
		return acc;
		
	}
	
	public static function foldRightWithIndex<A,B>(iter:Iterator<A>, f:A->B->Int->B, acc:B):B
	{
		
		
    var cur = 0;
		for (e in iter) {
			acc = f(e, acc, cur++);
		}
		return acc;
	}
	/*
   * foldl<A,B>(iter:Iterator<A>, f:B->A->B, acc:B):B
   */
	public static function foldLeft<A,B>(iter:Iterator<A>, f:B->A->B, acc:B):B
	{
		for (e in iter) {
			acc = f(acc, e);
		}
		return acc;
		
	}
	
	public static function foldLeftWithIndex<A,B>(iter:Iterator<A>, f:B->A->Int->B, acc:B):B
	{
		var i = 0;
		for (e in iter) {
			acc = f(acc, e, i++);
		}
		return acc;
	}
}

class FoldIterable {
	public static function foldRight <A,B>(iter:Iterable<A>, f:A->B->B, acc:B):B
	{
		return FoldIterator.foldRight(iter.iterator(), f, acc);
	}
	public static function foldLeft <A,B>(iter:Iterable<A>, f:B->A->B, acc:B):B
	{
		return FoldIterator.foldLeft(iter.iterator(), f, acc);
	}
	
	public static function foldLeftWithIndex <A,B>(iter:Iterable<A>, f:B->A->Int->B, acc:B):B
	{
		return FoldIterator.foldLeftWithIndex(iter.iterator(), f, acc);
	}
	public static function foldRightWithIndex <A,B>(iter:Iterable<A>, f:A->B->Int->B, acc:B):B
	{
		return FoldIterator.foldRightWithIndex(iter.iterator(), f, acc);
	}
}